﻿using FreeSql;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Sage.FreeSqlManager.Models;
using Sage.FreeSqlManager.Tenant;

namespace Sage.FreeSqlManager.Core
{
    /// <summary>
    /// FreeSql工厂实现 - 负责创建和管理FreeSql实例
    /// </summary>
    public class FreeSqlFactory : IFreeSqlFactory, IAsyncDisposable, IDisposable
    {
        // 跟踪资源是否已释放
        private bool _disposed = false;

        /// <summary>
        /// 存储所有创建的FreeSql实例
        /// </summary>
        /// <remarks>使用C#12的集合初始化语法</remarks>
        private readonly Dictionary<string, IFreeSql> _freeSqlInstances = [];

        /// <summary>
        /// 初始化FreeSql工厂（从IServiceProvider和配置中读取）
        /// </summary>
        /// <param name="serviceProvider">服务提供者</param>
        /// <param name="sectionName">配置节名称，默认为"DbConnections"</param>
        /// <exception cref="InvalidOperationException">配置错误时抛出</exception>
        public FreeSqlFactory(IServiceProvider serviceProvider, string sectionName = "DbConnections")
        {
            var configuration = serviceProvider.GetRequiredService<IConfiguration>();
            var configSection = configuration.GetSection(sectionName);

            if (!configSection.Exists())
            {
                throw new InvalidOperationException($"配置中未找到'{sectionName}'节");
            }

            if (!configSection.GetChildren().Any())
            {
                throw new InvalidOperationException($"配置节'{sectionName}'为空");
            }

            // 遍历配置中的所有连接
            foreach (var connectionConfig in configSection.GetChildren())
            {
                var name = connectionConfig.Key;

                // 获取连接字符串（必须项）
                var connectionString = connectionConfig.GetSection("ConnectionString").Value;
                if (string.IsNullOrEmpty(connectionString))
                {
                    throw new InvalidOperationException($"数据库'{name}'的连接字符串未配置");
                }

                // 获取数据库类型（字符串转枚举）
                var dataTypeString = connectionConfig.GetSection("DataType").Value ?? "MySql";
                var dataType = Enum.Parse<DataType>(dataTypeString, true); // 第二个参数true表示忽略大小写

                // 获取其他配置项
                bool useAutoSyncStructure = TryParseBool(connectionConfig.GetSection("UseAutoSyncStructure").Value, false);
                bool useNoneCommandParameter = TryParseBool(connectionConfig.GetSection("UseNoneCommandParameter").Value, true);
                bool monitorCommand = TryParseBool(connectionConfig.GetSection("MonitorCommand").Value, false);
                bool enableTenant = TryParseBool(connectionConfig.GetSection("EnableTenant").Value, false);

                // 创建配置对象
                var dbConfig = new DbConnectionConfig
                {
                    ConnectionString = connectionString,
                    DataType = dataType,
                    UseAutoSyncStructure = useAutoSyncStructure,
                    UseNoneCommandParameter = useNoneCommandParameter,
                    UseMonitorCommand = monitorCommand,
                    EnableTenant = enableTenant
                };

                // 创建并保存FreeSql实例
                _freeSqlInstances[name] = FreeSqlHelper.CreateFreeSqlInstance(dbConfig, name);
            }

            // 检查是否至少配置了一个数据库
            if (_freeSqlInstances.Count == 0)
            {
                throw new InvalidOperationException($"使用'{sectionName}'配置节构建数据库未成功");
            }
        }

        /// <summary>
        /// 初始化FreeSql工厂(传递配置字典)
        /// </summary>
        /// <param name="connectionConfigs">数据库连接配置字典，键为数据库名称</param>
        /// <exception cref="ArgumentException">连接配置为空或连接字符串未配置时抛出</exception>
        public FreeSqlFactory(Dictionary<string, DbConnectionConfig> connectionConfigs)
        {
            ArgumentNullException.ThrowIfNull(connectionConfigs);

            if (connectionConfigs.Count == 0)
            {
                throw new ArgumentException("至少需要提供一个数据库连接配置");
            }

            // 遍历所有连接配置
            foreach (var (name, config) in connectionConfigs)
            {
                if (string.IsNullOrEmpty(config.ConnectionString))
                {
                    throw new ArgumentException($"数据库'{name}'的连接字符串不能为空");
                }

                // 创建并保存FreeSql实例
                _freeSqlInstances[name] = FreeSqlHelper.CreateFreeSqlInstance(config, name);
            }
        }

        /// <summary>
        /// 获取指定名称的FreeSql实例
        /// </summary>
        /// <param name="dbName">数据库名称，对应配置中的键名</param>
        /// <returns>FreeSql实例</returns>
        /// <exception cref="ArgumentException">指定名称的数据库不存在时抛出</exception>
        public IFreeSql GetFreeSql(string dbName)
        {
            if (_freeSqlInstances.TryGetValue(dbName, out var freeSql))
            {
                return freeSql;
            }

            throw new ArgumentException($"无效的数据库名称: {dbName}。可用的数据库: {string.Join(", ", _freeSqlInstances.Keys)}");
        }

        /// <summary>
        /// 检查是否包含指定名称的数据库
        /// </summary>
        /// <param name="dbName">数据库名称</param>
        /// <returns>如果包含返回true，否则返回false</returns>
        public bool ContainsDatabase(string dbName)
        {
            return _freeSqlInstances.ContainsKey(dbName);
        }

        /// <summary>
        /// 创建租户作用域，临时切换租户上下文
        /// </summary>
        /// <param name="dbName">数据库名称</param>
        /// <param name="tenantId">租户ID</param>
        /// <returns>租户作用域，使用using语句确保资源释放</returns>
        /// <exception cref="ArgumentException">指定名称的数据库不存在时抛出</exception>
        public TenantScope CreateTenantScope(string dbName, long tenantId)
        {
            var freeSql = GetFreeSql(dbName);
            return new TenantScope(freeSql, tenantId);
        }

        /// <summary>
        /// 移除数据库的方法
        /// </summary>
        /// <param name="dbName"></param>
        public void RemoveFreeSql(string dbName)
        {
            if (_freeSqlInstances.TryGetValue(dbName, out var instance))
            {
                // 尝试释放资源
                (instance as IDisposable)?.Dispose();
                _freeSqlInstances.Remove(dbName);
            }
        }

        /// <summary>
        /// 尝试解析布尔值，失败时返回默认值
        /// </summary>
        /// <param name="value">要解析的字符串，可以为null</param>
        /// <param name="defaultValue">解析失败时的默认值</param>
        /// <returns>解析后的布尔值</returns>
        private static bool TryParseBool(string? value, bool defaultValue)
        {
            return bool.TryParse(value, out var result) ? result : defaultValue;
        }

        /// <summary>
        /// 释放
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this); // 在AOT中完全支持
        }

        /// <summary>
        /// 释放
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
                return;

            if (disposing)
            {
                foreach (var instance in _freeSqlInstances.Values)
                {
                    (instance as IDisposable)?.Dispose(); // AOT兼容的类型转换
                }
                _freeSqlInstances.Clear();
            }

            _disposed = true;
        }


        /// <summary>
        /// 添加异步释放方法
        /// </summary>
        /// <returns></returns>
        public async ValueTask DisposeAsync()
        {
            if (_disposed) return;

            foreach (var instance in _freeSqlInstances.Values)
            {
                if (instance is IAsyncDisposable asyncDisposable)
                    await asyncDisposable.DisposeAsync();
                else
                    (instance as IDisposable)?.Dispose();
            }
            _freeSqlInstances.Clear();
            _disposed = true;

            GC.SuppressFinalize(this);
        }
    }
}
